package com.neusoft.hifly.lock;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;

/**
 * 手写lock锁
 * <p>
 * 模拟业务：酒店开房卡，退房卡。开、退都需要CAS机制，保证原子性
 */
public class MyLock implements Lock {
	// 房卡、线程、原子性 -- Atomic包
	AtomicReference<Thread> owner = new AtomicReference<Thread>(); // 初始化都没有房卡，默认是null，
	// 阻塞队列，放那些没有抢到房卡的线程
	LinkedBlockingQueue<Thread> waiters = new LinkedBlockingQueue<Thread>();

	/**
	 * 加锁
	 */
	@Override
	public void lock() {
		// compareAndSet是CAS算法，第一个参数是期望值，若true，则赋值第二个参数，否则返回false
		// 这段的业务是判断owner是否抢到了房卡，若抢到了，不需要处理，若没抢到需要等待
		while (!owner.compareAndSet(null, Thread.currentThread())) {
			// 没有抢到房卡，将当前线程写入队列
			waiters.add(Thread.currentThread());

			// 强锁、阻塞
			LockSupport.park(); // 可以让当前线程阻塞。

			// 注意，这里不能用waiters.wait();方法。因为wait()方法的执行是在synchronized方法块里的。使用了wait()就作弊了，不是手写锁了。

			// 当前线程被唤醒后，执行下面方法，说明已经抢到了房卡，将移除队列
			waiters.remove(Thread.currentThread());
		}
	}

	/**
	 * 解锁
	 */
	@Override
	public void unlock() {
		// 解锁：也就是退房，要求只有当前房卡的持有者才能退房，其他人是不允许的。所以这里判断CAS
		if (owner.compareAndSet(Thread.currentThread(), null)) {
			// 解锁成功，唤醒其他线程（多个），非公平锁
			for (Object obj : waiters.toArray()) {
				Thread next = (Thread) obj;
				LockSupport.unpark(next); // 唤醒线程
			}
		} else {
			// 不是房卡持有者，不能解锁
		}

	}

	@Override
	public void lockInterruptibly() throws InterruptedException {
		// TODO Auto-generated method stub

	}

	@Override
	public boolean tryLock() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public Condition newCondition() {
		// TODO Auto-generated method stub
		return null;
	}

}
